The are many ways to test to see if a TCP port is open on another server. Some people use Telnet and some use Powershell cmdlets.
Telnet Pros:
- Legacy application and will work on older version of windows.
Telnet Cons:
- Telnet is not installed by default, and you must enable this feature.
- When you get a connection, most of the times you must close the prompt and start a new if you want to try a new one.
- Slow when it does not get a connection.
Test-NetConnection Pros:
- Easy to use when doing multiple ports or targets.
- Built in on server 2012 R2 and newer.
Test-NetConnection Cons:
- Only available on server 2012 R2 and newer.
- Slow when it does not get a connection.
So what do a mean with “Slow when it does not get a connection”? Well lets check how long it takes it takes for both these commands to timeout.
in powershell to test for telnet.
Measure-Command -Expression { Start-Process "C:\Windows\System32\telnet.exe" -ArgumentList "192.168.102.1 53" -NoNewWindow -Wait }
And for the Powershell cmdlet.
Measure-Command -Expression { Test-NetConnection -ComputerName 192.168.102.1 -Port 53 -InformationLevel Quiet}
We se that both takes about 21 seconds to execute. So if you have 10 ports to test on 100 servers, and all are closed it will almost take 6 days to test it. That is not a good way to go. Neither of these have a timeout value, so how can we speed it up? Can we use a .Net class? there is a class called TCPClient so lets look at what is has for methods.
There we se that we have 2 methods that seem likely (Connect and ConnectAsync), but when we look at them we don´t see any timeout. So lets see if anyone of them has something else we can use.
First we create a TCPObject from the .Net , then instead of just executing the command lets pipe it to get-member.
$TCPObject = new-Object system.Net.Sockets.TcpClient $TCPObject.Connect("192.168.102.1","53") | get-member
We see that the command Connect does a connection directly (takes long time) before we pipe it, so that we can´t use. Lets check Async. So lets close the connection, create a new and see what ASync gives us.
$TCPObject.Close()
$TCPObject = new-Object system.Net.Sockets.TcpClient
$TCPObject.ConnectAsync("192.168.102.1","53") | get-member
And we have a Method called wait, so what does that use for input?
$TCPObject.ConnectAsync("192.168.102.1","53").wait
Ahh.. it has a Timeout in and in Milliseconds. So lets try it out. We test a connection with timeout 5 seconds.
$TCPObject.ConnectAsync("192.168.102.1","53").wait(5000)
And we get the results False, so closed (or filtered).
The result was quicker then 21 seconds, but did the 5 seconds work? so lest measure it. So close the connection and create a new TCPObject. And the measure the command.
$TCPObject.Close() $TCPObject = new-Object system.Net.Sockets.TcpClient Measure-Command -Expression { $TCPObject.ConnectAsync("192.168.102.1","53").wait(5000) }
And we see that is is approx 5 seconds, so it is working.
So lets use that in a script.
First we need a function with three inputs, IPAddress(or hostname), Port and the Timeout value. Always remember to close the connection after a test.
Function CheckTCPPortStatus { [CmdletBinding()] Param( [Parameter(Mandatory=$True,Position=1)] [string]$IPAddress, [Parameter(Mandatory=$True,Position=2)] [string]$Port, [Parameter(Mandatory=$True,Position=3)] [int]$Timeout ) $TCPObject = new-Object system.Net.Sockets.TcpClient if($TCPObject.ConnectAsync($IPAddress,$Port).Wait($Timeout)) { $TCPObject.Close() return "Open" } else { $TCPObject.Close() return "Closed or Filtered" } }
Then we just need to create the script, and call the function.
So here is my full script